001 /*
002 * Copyright 2004-2005 Stephen J. McConnell.
003 * Copyright 2004 Apache Software Foundation
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
014 * implied.
015 *
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019
020 package net.dpml.transit;
021
022 import java.io.File;
023 import java.io.IOException;
024 import java.io.BufferedReader;
025 import java.io.InputStreamReader;
026
027 import java.util.Properties;
028 import java.util.Enumeration;
029
030
031 /**
032 * Encapsulates operating system and shell specific access to environment
033 * variables.
034 *
035 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
036 * @version 1.0.1
037 */
038 public class Environment extends Properties
039 {
040 /**
041 * os.name System property
042 */
043 public static final String OSNAME = System.getProperty( "os.name" );
044
045 /**
046 * user.name System property
047 */
048 public static final String USERNAME = System.getProperty( "user.name" );
049
050 /**
051 * the user's platform specific shell executable
052 */
053 private static String m_SHELL = null;
054
055 /**
056 * the last Env instance created
057 */
058 private static Environment m_INSTANCE = null;
059
060 /**
061 * Creates a snapshot of the current shell environment variables for a user.
062 *
063 * @throws EnvironmentException if there is an error accessing the environment
064 */
065 public Environment() throws EnvironmentException
066 {
067 Properties properties = getEnvVariables();
068 Enumeration list = properties.propertyNames();
069 while ( list.hasMoreElements() )
070 {
071 String key = ( String ) list.nextElement();
072 setProperty( key, properties.getProperty( key ) );
073 }
074 m_INSTANCE = this;
075 }
076
077 /**
078 * Gets a copy of the last Environment instance without parsing the user's shell
079 * environment. Use this method if you do not want to reparse the
080 * environment every time an environment variable is accessed. If an
081 * environment has not been created yet one is created then cloned
082 * and a copy is returned instead of returning null.
083 *
084 * @return a copy of the last Environment object created
085 * @throws EnvironmentException if there is an error accessing the environment
086 */
087 Environment getLastEnv() throws EnvironmentException
088 {
089 if( m_INSTANCE == null )
090 {
091 m_INSTANCE = new Environment();
092 }
093
094 // return cloned copy so there is no cross interference
095 return ( Environment ) m_INSTANCE.clone();
096 }
097
098
099 /**
100 * Gets the value of a shell environment variable.
101 *
102 * @param name the name of variable
103 * @return the String representation of an environment variable value
104 * @throws EnvironmentException if there is a problem accessing the environment
105 */
106 public static String getEnvVariable( String name )
107 throws EnvironmentException
108 {
109 String osName = System.getProperty( "os.name" );
110
111 if( isUnix() )
112 {
113 Properties properties = getUnixShellVariables();
114 return properties.getProperty( name );
115 }
116 else if( isWindows() )
117 {
118 return getWindowsShellVariable( name );
119 }
120
121 throw new EnvironmentException( name,
122 "Unrecognized operating system: " + osName );
123 }
124
125 /**
126 * Checks to see if the operating system is a UNIX variant.
127 *
128 * @return true of the OS is a UNIX variant, false otherwise
129 */
130 public static boolean isUnix()
131 {
132 if( -1 != OSNAME.indexOf( "Linux" )
133 || -1 != OSNAME.indexOf( "SunOS" )
134 || -1 != OSNAME.indexOf( "Solaris" )
135 || -1 != OSNAME.indexOf( "MPE/iX" )
136 || -1 != OSNAME.indexOf( "AIX" )
137 || -1 != OSNAME.indexOf( "FreeBSD" )
138 || -1 != OSNAME.indexOf( "Irix" )
139 || -1 != OSNAME.indexOf( "Digital Unix" )
140 || -1 != OSNAME.indexOf( "HP-UX" )
141 || -1 != OSNAME.indexOf( "Mac OS X" ) )
142 {
143 return true;
144 }
145
146 return false;
147 }
148
149
150 /**
151 * Checks to see if the operating system is a Windows variant.
152 *
153 * @return true of the OS is a Windows variant, false otherwise
154 */
155 public static boolean isWindows()
156 {
157 return ( -1 != OSNAME.indexOf( "Windows" ) );
158 }
159
160 /**
161 * Checks to see if the operating system is NetWare.
162 *
163 * @return true of the OS is NetWare, false otherwise
164 */
165 public static boolean isNetWare()
166 {
167 return ( -1 != OSNAME.indexOf( "netware" ) );
168 }
169
170 /**
171 * Checks to see if the operating system is OpenVMS.
172 *
173 * @return true of the OS is a NetWare variant, false otherwise
174 */
175 public static boolean isOpenVMS()
176 {
177 return ( -1 != OSNAME.indexOf( "openvms" ) );
178 }
179
180 /**
181 * Gets all environment variables within a Properties instance where the
182 * key is the environment variable name and value is the value of the
183 * property.
184 *
185 * @return the environment variables and values as Properties
186 * @throws EnvironmentException if os is not recognized
187 */
188 public static Properties getEnvVariables() throws EnvironmentException
189 {
190 if( isUnix() )
191 {
192 return getUnixShellVariables();
193 }
194
195 if( isWindows() )
196 {
197 return getWindowsShellVariables();
198 }
199
200 throw new EnvironmentException(
201 new UnsupportedOperationException( "Environment operations not "
202 + "supported on unrecognized operatings system" ) );
203 }
204
205
206 /**
207 * Gets the user's shell executable.
208 *
209 * @return the shell executable for the user
210 * @throws EnvironmentException the there is a problem accessing shell
211 * information
212 */
213 public static String getUserShell() throws EnvironmentException
214 {
215 if( -1 != OSNAME.indexOf( "Mac OS X" ) )
216 {
217 return getMacUserShell();
218 }
219
220 if( isWindows() )
221 {
222 return getWindowsUserShell();
223 }
224
225 throw new EnvironmentException(
226 new UnsupportedOperationException( "Environment operations not "
227 + "supported on unrecognized operatings system" ) );
228 }
229
230 // ------------------------------------------------------------------------
231 // Private UNIX Shell Operations
232 // ------------------------------------------------------------------------
233
234 /**
235 * Gets the default login shell used by a mac user.
236 *
237 * @return the Mac user's default shell as referenced by cmd:
238 * 'nidump passwd /'
239 * @throws EnvironmentException if os information is not resolvable
240 */
241 private static String getMacUserShell()
242 throws EnvironmentException
243 {
244 Process process = null;
245 BufferedReader reader = null;
246
247 if( null != m_SHELL )
248 {
249 return m_SHELL;
250 }
251
252 try
253 {
254 String entry = null;
255 String [] args = {"nidump", "passwd", "/"};
256 process = Runtime.getRuntime().exec( args );
257 reader = new BufferedReader(
258 new InputStreamReader( process.getInputStream() ) );
259
260 while ( null != ( entry = reader.readLine() ) )
261 {
262 // Skip entries other than the one for this username
263 if( !entry.startsWith( USERNAME ) )
264 {
265 continue;
266 }
267
268 // Get the shell part of the passwd entry
269 int index = entry.lastIndexOf( ':' );
270
271 if( index == -1 )
272 {
273 throw new EnvironmentException(
274 "passwd database contains malformed user entry for "
275 + USERNAME );
276 }
277
278 m_SHELL = entry.substring( index + 1 );
279 return m_SHELL;
280 }
281
282 process.waitFor();
283 reader.close();
284 }
285 catch( Throwable t )
286 {
287 throw new EnvironmentException( t );
288 }
289 finally
290 {
291 if( process != null )
292 {
293 process.destroy();
294 }
295
296 try
297 {
298 if( null != reader )
299 {
300 reader.close();
301 }
302 }
303 catch( IOException e )
304 {
305 // do nothing
306 final boolean ignorable = true;
307 }
308 }
309
310 throw new EnvironmentException( "User " + USERNAME
311 + " is not present in the passwd database" );
312 }
313
314
315 /**
316 * Adds a set of Windows variables to a set of properties.
317 * @return the environment properties
318 * @exception EnvironmentException if an error occurs
319 */
320 private static Properties getUnixShellVariables()
321 throws EnvironmentException
322 {
323 Process process = null;
324 Properties properties = new Properties();
325
326 // Read from process here
327 BufferedReader reader = null;
328
329 // fire up the shell and get echo'd results on stdout
330 try
331 {
332 String [] args = {getUnixEnv()};
333 process = Runtime.getRuntime().exec( args );
334 reader = new BufferedReader(
335 new InputStreamReader( process.getInputStream() ) );
336
337 String line = null;
338 while ( null != ( line = reader.readLine() ) )
339 {
340 int index = line.indexOf( '=' );
341
342 if( -1 == index )
343 {
344 if( line.length() != 0 )
345 {
346 System.err.println(
347 "Skipping line - could not find '=' in"
348 + " line: '" + line + "'" );
349 }
350 continue;
351 }
352
353 String name = line.substring( 0, index );
354 String value = line.substring( index + 1, line.length() );
355 properties.setProperty( name, value );
356 }
357
358 process.waitFor();
359 reader.close();
360 }
361 catch( Throwable t )
362 {
363 throw new EnvironmentException( "NA", t );
364 }
365 finally
366 {
367 process.destroy();
368
369 try
370 {
371 if( null != reader )
372 {
373 reader.close();
374 }
375 }
376 catch( IOException e )
377 {
378 // ignore
379 final boolean ignorable = true;
380 }
381 }
382
383 // Check that we exited normally before returning an invalid output
384 if( 0 != process.exitValue() )
385 {
386 throw new EnvironmentException(
387 "Environment process failed "
388 + " with non-zero exit code of "
389 + process.exitValue() );
390 }
391
392 return properties;
393 }
394
395
396 /**
397 * Gets the UNIX env executable path.
398 *
399 * @return the absolute path to the env program
400 * @throws EnvironmentException if it cannot be found
401 */
402 private static String getUnixEnv() throws EnvironmentException
403 {
404 File env = new File( "/bin/env" );
405
406 if( env.exists() && env.canRead() && env.isFile() )
407 {
408 return env.getAbsolutePath();
409 }
410
411 env = new File( "/usr/bin/env" );
412 if( env.exists() && env.canRead() && env.isFile() )
413 {
414 return env.getAbsolutePath();
415 }
416
417 throw new EnvironmentException(
418 "Could not find the UNIX env executable" );
419 }
420
421
422 // ------------------------------------------------------------------------
423 // Private Windows Shell Operations
424 // ------------------------------------------------------------------------
425
426
427 /**
428 * Gets the shell used by the Windows user.
429 *
430 * @return the shell: cmd.exe or command.com.
431 */
432 private static String getWindowsUserShell()
433 {
434 if( null != m_SHELL )
435 {
436 return m_SHELL;
437 }
438
439 if( -1 != OSNAME.indexOf( "98" )
440 || -1 != OSNAME.indexOf( "95" )
441 || -1 != OSNAME.indexOf( "Me" ) )
442 {
443 m_SHELL = "command.com";
444 return m_SHELL;
445 }
446
447 m_SHELL = "cmd.exe";
448 return m_SHELL;
449 }
450
451
452 /**
453 * Adds a set of Windows variables to a set of properties.
454 * @return the environment properties
455 * @exception EnvironmentException if an error occurs
456 */
457 private static Properties getWindowsShellVariables()
458 throws EnvironmentException
459 {
460 String line = null;
461 Process process = null;
462 BufferedReader reader = null;
463 Properties properties = new Properties();
464
465 // build the command based on the shell used: cmd.exe or command.com
466 StringBuffer buffer = new StringBuffer( getWindowsUserShell() );
467 buffer.append( " /C SET" );
468
469 // fire up the shell and get echo'd results on stdout
470 try
471 {
472 process = Runtime.getRuntime().exec( buffer.toString() );
473 reader = new BufferedReader(
474 new InputStreamReader( process.getInputStream() ) );
475 while ( null != ( line = reader.readLine() ) )
476 {
477 int index = line.indexOf( '=' );
478
479 if( -1 == index )
480 {
481 System.err.println( "Skipping line - could not find '=' in"
482 + " line: '" + line + "'" );
483 continue;
484 }
485
486 String name = line.substring( 0, index );
487 String value = line.substring( index + 1, line.length() );
488 properties.setProperty( name, value );
489 }
490
491 process.waitFor();
492 reader.close();
493 }
494 catch( Throwable t )
495 {
496 throw new EnvironmentException( t );
497 }
498 finally
499 {
500 process.destroy();
501
502 try
503 {
504 if( null != reader )
505 {
506 reader.close();
507 }
508 }
509 catch( IOException e )
510 {
511 // ignore
512 final boolean ignorable = true;
513 }
514 }
515
516 if( 0 != process.exitValue() )
517 {
518 throw new EnvironmentException( "Environment process failed"
519 + " with non-zero exit code of " + process.exitValue() );
520 }
521
522 return properties;
523 }
524
525
526 /**
527 * Gets the value for a windows command shell environment variable.
528 *
529 * @param name the name of the variable
530 * @return the value of the variable
531 * @throws EnvironmentException if there is an error accessing the value
532 */
533 private static String getWindowsShellVariable( String name )
534 throws EnvironmentException
535 {
536 String value = null;
537 Process process = null;
538 BufferedReader reader = null;
539
540 StringBuffer buffer = new StringBuffer( getWindowsUserShell() );
541 buffer.append( " /C echo %" );
542 buffer.append( name );
543 buffer.append( '%' );
544
545 // fire up the shell and get echo'd results on stdout
546 try
547 {
548 process = Runtime.getRuntime().exec( buffer.toString() );
549 reader = new BufferedReader(
550 new InputStreamReader( process.getInputStream() ) );
551 value = reader.readLine();
552 process.waitFor();
553 reader.close();
554 }
555 catch( Throwable t )
556 {
557 throw new EnvironmentException( name, t );
558 }
559 finally
560 {
561 process.destroy();
562
563 try
564 {
565 if( null != reader )
566 {
567 reader.close();
568 }
569 }
570 catch( IOException e )
571 {
572 // ignore
573 final boolean ignorable = true;
574 }
575 }
576
577 if( 0 == process.exitValue() )
578 {
579 // Handle situations where the env property does not exist.
580 if( value.startsWith( "%" ) && value.endsWith( "%" ) )
581 {
582 return null;
583 }
584
585 return value;
586 }
587
588 throw new EnvironmentException(
589 name,
590 "Environment process failed"
591 + " with non-zero exit code of "
592 + process.exitValue() );
593 }
594 }